﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Collections.Concurrent;

namespace comm
{
    #region TimelineState
    public enum TimelineState
    {
        END = 0,
        ENDING = 1,
        BEGIN = 2,
        HALT = 3
    }
    #endregion

    #region Timeline
    public class Timeline
    {
        public uint TotalTime { get; private set; }
        public uint BeginTime { get; private set; }
        public uint? EndTime { get; private set; }
        public uint? MaxLoopCount { get; private set; }
        public uint StartType { get; private set; }
        public bool Complete { get; private set; }
        public uint TimeIndex { get; private set; }
        public object Tag { get; set; }

        public Action<Timeline> OnEnterTimeCallback = null;
        public Action<Timeline> OnCompleteCallback = null;

        int _timesCount = 0, _loopCount = 0;
        double _countMS = 0, _nextUseMS = 0, _nextNeedMS = 0;
        uint[] _times = null;
        bool _begin = false;
        bool _first = true;

        public Timeline(uint[] times, uint startType = 0, uint beginTime = 0, uint? endTime = null, uint? maxLoopCount = null)
        {
            BeginTime = beginTime;
            EndTime = endTime;
            MaxLoopCount = maxLoopCount;
            StartType = Math.Min(1, startType);

            if (StartType == 1 && endTime == null)
            {
                MaxLoopCount = 1;
            }

            _nextUseMS -= beginTime;
            _times = times;
            _timesCount = _times.Length;

            foreach (var time in _times)
            {
                TotalTime += time;
            }

            if (_timesCount > 0 && MaxLoopCount != 0)
            {
                _nextNeedMS = times[TimeIndex];
                _begin = true;
            }
        }

        public void Update(TimelineState state, TimeStat timeStat)
        {
            var b = true;
            if (StartType == 1)
            {
                b = state == TimelineState.ENDING;
            }
            if (b && _begin)
            {
                var ms = timeStat.ElapsedTime.TotalMilliseconds;
                _nextUseMS += ms;
                _countMS += ms;

                if (_first)
                {
                    _first = false;
                    if (OnEnterTimeCallback != null)
                    {
                        OnEnterTimeCallback(this);
                    }
                }

                #region check EndTime
                if (EndTime != null && _countMS > EndTime)
                {
                    _begin = false;
                    Complete = true;
                    if (OnCompleteCallback != null)
                    {
                        OnCompleteCallback(this);
                    }
                    return;
                }
                #endregion

                while (_nextUseMS > _nextNeedMS)
                {
                    _nextUseMS -= _nextNeedMS;
                    if (TimeIndex == _timesCount - 1)
                    {
                        TimeIndex = 0;
                        _loopCount++;
                        #region check LoopCount
                        if (MaxLoopCount != null && _loopCount == MaxLoopCount)
                        {
                            _begin = false;
                            Complete = true;

                            if (OnCompleteCallback != null)
                            {
                                OnCompleteCallback(this);
                            }
                            break;
                        }
                        #endregion
                    }
                    else
                    {
                        TimeIndex++;
                    }
                    _nextNeedMS = _times[TimeIndex];

                    if (OnEnterTimeCallback != null)
                    {
                        OnEnterTimeCallback(this);
                    }
                }
            }
        }
    }
    #endregion

    #region MainTineline
    public class MainTimeline
    {
        public TimelineState State { get; private set; }

        Dictionary<string, Timeline> _timelineDic = new Dictionary<string, Timeline>();
        comm.TimeStat _timeStat = new comm.TimeStat();
        List<Timeline> _zeroList = new List<Timeline>();
        List<Timeline> _endList = new List<Timeline>();

        public MainTimeline()
        {
        }

        public void AddLine(string key, Timeline line)
        {
            if (State == TimelineState.END && !_timelineDic.ContainsKey(key))
            {
                _timelineDic.Add(key, line);
            }
        }
        public void RemoveLine(string key)
        {
            if (State == TimelineState.END && _timelineDic.ContainsKey(key))
            {
                _timelineDic[key].OnEnterTimeCallback = null;
                _timelineDic.Remove(key);
            }
        }
        public void ClearAllLine()
        {
            if (State == TimelineState.END)
            {
                foreach (var line in _timelineDic.Values)
                {
                    line.OnEnterTimeCallback = null;
                }
                _timelineDic.Clear();
            }
        }

        public void Begin()
        {
            State = TimelineState.END;
            _zeroList.Clear();
            _endList.Clear();
            foreach (var line in _timelineDic.Values)
            {
                if (line.StartType == 0)
                {
                    _zeroList.Add(line);
                }
                else
                {
                    _endList.Add(line);
                }
            }
            State = TimelineState.BEGIN;
        }
        public void TryEnd()
        {
            State = TimelineState.ENDING;
        }
        public void End()
        {
            State = TimelineState.END;
            _timeStat.Reset();
        }
        public void Halt()
        {
            State = TimelineState.HALT;
            _timeStat.Reset();
        }

        public void Update()
        {
            if (State != TimelineState.HALT && State != TimelineState.END)
            {
                _timeStat.Update();

                if (State == TimelineState.BEGIN)
                {
                    var complete = 0;
                    foreach (var item in _zeroList)
                    {
                        item.Update(State, _timeStat);
                        complete = item.Complete ? 1 : 0;
                    }
                    if (complete == _zeroList.Count)
                    {
                        State = TimelineState.ENDING;
                    }
                }

                if (State == TimelineState.ENDING)
                {
                    var complete = 0;
                    foreach (var item in _endList)
                    {
                        item.Update(State, _timeStat);
                        complete = item.Complete ? 1 : 0;
                    }
                    if (complete == _zeroList.Count)
                    {
                        State = TimelineState.END;
                    }
                }
            }
        }
    }
    #endregion

    #region TimeStat
    public class TimeStat
    {
        public int FPS { get; private set; }
        public TimeSpan ElapsedTime { get; private set; }

        DateTime? _lastUpdateTime = null;
        int _sencondUpdateCount = 0;
        Timing _fpsTiming = new Timing();

        public void Reset()
        {
            _lastUpdateTime = null;
            _sencondUpdateCount = 0;
            _fpsTiming.Reset();
        }
        public void Update()
        {
            if (_lastUpdateTime == null)
            {
                _lastUpdateTime = DateTime.Now;
            }
            var now = DateTime.Now;
            ElapsedTime = now - _lastUpdateTime.Value;
            _lastUpdateTime = now;

            _sencondUpdateCount++;
            if (_fpsTiming.GetElapsedTime(this, 1000) > 0)
            {
                FPS = _sencondUpdateCount;
                _sencondUpdateCount = 0;
            }
        }
    }
    #endregion

    #region Timing
    public class Timing
    {
        private double _msCount = 0;

        public void Reset()
        {
            _msCount = 0;
        }
        public int GetElapsedTime(TimeStat timeStat, double millisecond)
        {
            _msCount += timeStat.ElapsedTime.TotalMilliseconds;
            int count = (int)Math.Round(_msCount / millisecond);
            _msCount -= count * millisecond;
            return count;
        }
    }
    #endregion
}
